import appEventEnum from "../enum/appEvent.enum"
import appTypeEnum from "../enum/appType.enum"
import phaseEnum from "../enum/phase.enum"
import { GetAppPhaseTimesCommand, GetAppStateCommand } from "../command/app.command"
import { SdkObservable } from "./observable"
import { ClassFactory, deepDiffers } from "./utils"

class AppStateSubFactory {
  create(e, t) {
    return t.onChanged(() => e.onChanged())
  }
}
class AppStateObservable {
  _data: { phase: string; phaseTimes: {}; application: string }
  AppPhase: any
  appPhaseConverter: any
  applicationConverter: any
  constructor(e, t) {
    this._data = { phase: phaseEnum.UNINITIALIZED, phaseTimes: {}, application: appTypeEnum.SHOWCASE }
    this.AppPhase = e.AppPhase
    this.appPhaseConverter = t.appPhaseConverter
    this.applicationConverter = t.applicationConverter
  }
  get data() {
    return this._data
  }
  equals(e) {
    return !deepDiffers(this, e)
  }
  copy(e) {
    this._data.phase = e.data.phase
    // (function (e, t, ...o) {
    //   for (const n of o) e[n] = t[n]
    // })(this.data.phaseTimes, e.data.phaseTimes),
    this._data.application = e.data.application
  }
  update(e) {
    this._data.phase = this.appPhaseConverter.toSdkAppPhase(e.phase)
    this._data.application = this.applicationConverter.toSdkApplication(e.application)
    this._data.phaseTimes[phaseEnum.WAITING] = e.phaseTimes[this.AppPhase.WAITING]
    this._data.phaseTimes[phaseEnum.LOADING] = e.phaseTimes[this.AppPhase.LOADING]
    this._data.phaseTimes[phaseEnum.PLAYING] = e.phaseTimes[this.AppPhase.PLAYING]
    this._data.phaseTimes[phaseEnum.STARTING] = e.phaseTimes[this.AppPhase.STARTING]
    this._data.phaseTimes[phaseEnum.UNINITIALIZED] = e.phaseTimes[this.AppPhase.UNINITIALIZED]
    this._data.phaseTimes[phaseEnum.ERROR] = e.phaseTimes[this.AppPhase.ERROR]
  }
}
export default {
  Phase: sdk => sdk.addEnumToInterface({ namespace: "App", name: "Phase", values: phaseEnum }),
  Event: sdk => sdk.addEnumToInterface({ namespace: "App", name: "Event", values: appEventEnum }),
  Application: sdk => sdk.addEnumToInterface({ namespace: "App", name: "Application", values: appTypeEnum }),
  getState: (sdk, allFunc, dependencies) => {
    dependencies.getDependencies().then(([e]) => {
      allFunc.addBinding(GetAppStateCommand, async () => {
        try {
          const o = e.getData()
          return {
            phase: allFunc.appPhaseConverter.toSdkAppPhase(o.phase),
            application: allFunc.applicationConverter.toSdkApplication(o.application)
          }
        } catch (e) {
          throw Error("Error: Can't get application data at this time")
        }
      })
    })
    sdk.addCommandCreator({ namespace: "App", name: "getState", args: [] }, () => new GetAppStateCommand())
  },
  getLoadTimes: (sdk, allFunc, dependencies) => {
    allFunc.addBinding(GetAppPhaseTimesCommand, async () => {
      const phaseData = {
          [phaseEnum.WAITING]: null,
          [phaseEnum.LOADING]: null,
          [phaseEnum.STARTING]: null,
          [phaseEnum.PLAYING]: null,
          [phaseEnum.ERROR]: null
        },
        [phase] = await dependencies.getDependencies()
      for (const val in phase.phaseTimes) {
        phaseData[allFunc.appPhaseConverter.toSdkAppPhase(Number(val))] = phase.phaseTimes[Number(val)]
      }
      return phaseData
    })
    sdk.addCommandCreator({ namespace: "App", name: "getLoadTimes", args: [] }, () => new GetAppPhaseTimesCommand())
  },
  state: (sdk, exportOBJ, allFunc, dependencies) => {
    const observable = SdkObservable.create(dependencies, new AppStateSubFactory(), new ClassFactory(AppStateObservable, exportOBJ, allFunc))
    sdk.addObservableToInterface({ namespace: "App", name: "state" }, observable)
  },
  onEvent: (sdk, exportOBJ, allFunc) => {
    const resule = e => [allFunc.appPhaseConverter.toSdkAppPhase(e.phase)]
    allFunc.subscribe(exportOBJ.AppPhaseChangeMessage, val => {
      sdk.broadcast(appEventEnum.PHASE_CHANGE, resule, val)
    })
    allFunc.subscribe(exportOBJ.ModelLoadProgressMessage, val => {
      const { loaded, total } = val
      sdk.broadcast(phaseEnum.LOADING, () => [
        {
          loaded,
          total
        }
      ])
    })
  }
}
